package com.cfks.lanscript;

import java.util.regex.*;
import java.util.*;
import java.lang.*;

class LanScriptCompiler {
  static List<String> importList = new ArrayList<>();
  static boolean inFun = false;

  private static List<Object> parseLanScript(String code) {
    String regex = "//\\*(?:(?!\\*/).|[\\r\\n])*?\\*/|[ \\t]*//.*"; // 去注释
    String[] lines = code.replaceAll(regex, "").split("\n");

    List<Object> ast = new ArrayList<>();

    for (String line : lines) {
      line = line.trim();

      // Ignore empty lines
      if (line.isEmpty()) {
        continue;
      }

      Pattern pattern = Pattern.compile("\\)[ \\t]*::$");
      if (!line.endsWith(";") && !pattern.matcher(line).find() && !line.endsWith("::")) {
        throw new RuntimeException(line + "\n" + fastSpace(line.length() + 1) + " ^ 缺少\";\"");
      }

      // Parse function internal statements
      if (inFun && !line.equals("::")) {
        Object lastItem = ast.get(ast.size() - 1);
        String[] statement = line.substring(0, line.length() - 2).split("\\(");
        if (statement.length > 0 && !statement[0].isEmpty()) {
          if (lastItem instanceof Variable) {
            ((Variable) lastItem).body.add(callFunAST(statement[0], statement[1]));
          } else if (lastItem instanceof Function) {
            ((Function) lastItem).body.add(callFunAST(statement[0], statement[1]));
          }
        }
      }

      // Parse variable declarations
      if (line.startsWith("public") && !line.startsWith("public fun")) {
        String[] parts = line.substring(0, line.length() - 1).split(" "); // Remove semicolon
        String name = parts[1];
        String type = parts[3];
        String value = parts.length > 5 ? parts[5] : getValDefValue(type);

        ast.add(new Variable(name, type, value));
      } else if (line.startsWith("public fun")) {
        inFun = true;
        String[] parts = line.split(" ");
        String name = parts[3].substring(0, parts[3].length() - 1).split("\\(")[0];
        String params = parts[3].substring(0, parts[3].length() - 2).split("\\(")[1];
        params = params.substring(0, params.length() - 1).replaceAll(" ", "");
        params = params.isEmpty() ? "void" : params;

        String returnType = parts[2];
        List<Object> body = new ArrayList<>();

        ast.add(new Function(name, body, returnType, params));
      } else if (line.equals("::")) {
        inFun = false;
      } else if (line.startsWith("use")) {
        String[] parts = line.substring(0, line.length() - 1).split(" ");
        importList.add(parts[1]);
      } else if (line.contains("(") && line.contains(");") && !inFun) {
        // 调用语句
        String[] statement = line.substring(0, line.length() - 1).split("\\(");
        if (statement.length > 0 && !statement[0].isEmpty()) {
          ast.add(callFunAST2(statement[0], statement[1].substring(0, statement[1].length() - 1)));
        }
      }
    }

    return ast;
  }

  private static FunctionCall callFunAST(String name, String params) {
    String importPack = "";
    for (String importName : importList) {
      if (importName.split("\\.")[importName.split("\\.").length - 1].equals(
          name.split("\\.")[0])) {
        importPack = importName;
        break;
      }
    }
    return new FunctionCall(
        !importPack.replaceAll("\\.", "/").isEmpty()
            ? importPack.replaceAll("\\.", "/")
                + "/"
                + name.replaceAll(
                        importPack.split("\\.")[importPack.split("\\.").length - 1] + "\\.", "")
                    .replaceAll("\\.", "/")
            : name.replaceAll("\\.", "/"),
        params.replaceAll(" ", "").replaceAll(",", " ").isEmpty()
            ? "void"
            : params.replaceAll(" ", "").replaceAll(",", " "));
  }

  private static FunctionCall callFunAST2(String name, String params) {
    String importPack = "";
    for (String importName : importList) {
      if (importName.split("\\.")[importName.split("\\.").length - 1].equals(
          name.split("\\.")[0])) {
        importPack = importName;
        break;
      }
    }
    return new FunctionCall(
        !importPack.replaceAll("\\.", "/").isEmpty()
            ? importPack.replaceAll("\\.", "/")
                + "/"
                + name.replaceAll(
                        importPack.split("\\.")[importPack.split("\\.").length - 1] + "\\.", "")
                    .replaceAll("\\.", "/")
            : name.replaceAll("\\.", "/"),
        params.replaceAll(" ", "").replaceAll(",", " ").isEmpty()
            ? ""
            : params.replaceAll(" ", "").replaceAll(",", " "));
  }

  private static String generateLSC(List<Object> ast) {
    StringBuilder lsoCode = new StringBuilder();

    for (Object item : ast) {
      if (item instanceof Variable) {
        Variable variable = (Variable) item;
        lsoCode
            .append("public val ")
            .append(variable.name)
            .append(" ")
            .append(variable.dataType)
            .append(" ")
            .append(variable.value)
            .append("\n");
      } else if (item instanceof Function) {
        Function function = (Function) item;
        lsoCode
            .append("public fun ")
            .append(function.returnType)
            .append(" ")
            .append(function.name)
            .append(" ")
            .append(function.params)
            .append(" funstart\n");
        for (Object statement : function.body) {
          FunctionCall call = (FunctionCall) statement;
          lsoCode
              .append("F")
              .append(call.name)
              .append(" ")
              .append(call.params.equals("void") ? "void" : "val:" + call.params)
              .append("\n");
        }
        lsoCode.append("funend\n");
      } else if (item instanceof FunctionCall) {
        FunctionCall call = (FunctionCall) item;
        String path = call.name;
        if (!path.contains("/")) {
          path = "this/" + path;
        }
        lsoCode
            .append("F")
            .append(path)
            .append(" ")
            .append(call.params.isEmpty() ? "" : "val:" + call.params)
            .append("\n");
      }
    }

    return lsoCode.toString();
  }

  private static String getValDefValue(String type) {
    switch (type) {
      case "String":
        return "\"\"";
      case "Number":
        return "0";
      default:
        return "null";
    }
  }

  private static String fastSpace(int num) {
    return " ".repeat(Math.max(0, num));
  }

  public static String compile(String code) {
    importList.clear();
    List<Object> ast = parseLanScript(code);
    return generateLSC(ast);
  }

  private static class Variable {
    String name;
    String dataType;
    String value;
    List<Object> body = new ArrayList<>();

    Variable(String name, String dataType, String value) {
      this.name = name;
      this.dataType = dataType;
      this.value = value;
    }
  }

  private static class Function {
    String name;
    List<Object> body;
    String returnType;
    String params;

    Function(String name, List<Object> body, String returnType, String params) {
      this.name = name;
      this.body = body;
      this.returnType = returnType;
      this.params = params;
    }
  }

  private static class FunctionCall {
    String name;
    String params;

    FunctionCall(String name, String params) {
      this.name = name;
      this.params = params;
    }
  }
}
